/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.mybatis.injector.methods;

import static java.util.stream.Collectors.joining;

import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.elsfs.cloud.common.annotations.Spell;

/**
 * 查询满足条件所有数据
 *
 * @author zeng
 */
@Slf4j
public class SelectList extends com.baomidou.mybatisplus.core.injector.methods.SelectList {
  @Override
  public MappedStatement injectMappedStatement(
      Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
    List<TableFieldInfo> list = tableInfo.getFieldList();
    Optional<TableFieldInfo> first =
        list.stream()
            .filter(tableFieldInfo -> tableFieldInfo.getField().isAnnotationPresent(Spell.class))
            .findFirst();
    if (first.isEmpty()) {
      return super.injectMappedStatement(mapperClass, modelClass, tableInfo);
    }
    TableFieldInfo info = first.get();
    Spell annotation = info.getField().getAnnotation(Spell.class);
    if (!annotation.isWhere()) {
      return super.injectMappedStatement(mapperClass, modelClass, tableInfo);
    }
    SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
    String sql =
        String.format(
            sqlMethod.getSql(),
            sqlFirst(),
            sqlSelectColumns(tableInfo, true),
            tableInfo.getTableName(),
            sqlWhereEntityWrapper(true, tableInfo, info),
            sqlOrderBy(tableInfo),
            sqlComment());
    SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);
    return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo);
  }

  /**
   * EntityWrapper方式获取select where
   *
   * @param newLine 是否提到下一行
   * @param table 表信息
   * @param info 字段信息
   * @return String
   */
  @SuppressWarnings("all")
  protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table, TableFieldInfo info) {
    //  Wrapper SQL
    String _sgEs_ =
        "<bind name=\"_sgEs_\" value=\"ew.sqlSegment != null and ew.sqlSegment != ''\"/>";
    String andSqlSegment =
        SqlScriptUtils.convertIf(
            String.format(" AND ${%s}", WRAPPER_SQLSEGMENT),
            String.format("_sgEs_ and %s", WRAPPER_NONEMPTYOFNORMAL),
            true);
    String lastSqlSegment =
        SqlScriptUtils.convertIf(
            String.format(" ${%s}", WRAPPER_SQLSEGMENT),
            String.format("_sgEs_ and %s", WRAPPER_EMPTYOFNORMAL),
            true);

    /*
     * 存在逻辑删除 SQL 注入
     */
    if (table.isWithLogicDelete()) {
      String sqlScript = getAllSqlWhere(table, info, true, true, true, WRAPPER_ENTITY_DOT);
      sqlScript =
          SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER_ENTITY), true);
      sqlScript =
          SqlScriptUtils.convertIf(
              _sgEs_ + NEWLINE + sqlScript + NEWLINE + andSqlSegment + NEWLINE + lastSqlSegment,
              String.format("%s != null", WRAPPER),
              true);
      sqlScript =
          SqlScriptUtils.convertWhere(table.getLogicDeleteSql(false, true) + NEWLINE + sqlScript);
      return newLine ? NEWLINE + sqlScript : sqlScript;
    }

    /*
     * 普通 SQL 注入
     */
    String sqlScript = getAllSqlWhere(table, info, false, false, true, WRAPPER_ENTITY_DOT);
    sqlScript =
        SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER_ENTITY), true);
    sqlScript =
        SqlScriptUtils.convertWhere(sqlScript + NEWLINE + andSqlSegment) + NEWLINE + lastSqlSegment;
    sqlScript =
        SqlScriptUtils.convertIf(
            _sgEs_ + NEWLINE + sqlScript, String.format("%s != null", WRAPPER), true);
    return newLine ? NEWLINE + sqlScript : sqlScript;
  }

  /**
   * 获取所有的查询的 sql 片段
   *
   * @param fistAnd 首个条件是否添加 AND 关键字
   * @param ignoreLogicDelFiled 是否过滤掉逻辑删除字段
   * @param withId 是否包含 id 项
   * @param prefix 前缀
   * @return sql 脚本片段
   */
  public String getAllSqlWhere(
      TableInfo table,
      TableFieldInfo info,
      boolean fistAnd,
      boolean ignoreLogicDelFiled,
      boolean withId,
      final String prefix) {
    final String newPrefix = prefix == null ? EMPTY : prefix;
    String filedSqlScript =
        table.getFieldList().stream()
            .filter(
                i -> {
                  if (info == i) {
                    return false;
                  }
                  if (ignoreLogicDelFiled) {
                    return !(table.isWithLogicDelete() && i.isLogicDelete());
                  }
                  return true;
                })
            .map(i -> i.getSqlWhere(newPrefix))
            .filter(Objects::nonNull)
            .collect(joining(NEWLINE));
    if (!withId || StringUtils.isBlank(table.getKeyProperty())) {
      return filedSqlScript;
    }
    String newKeyProperty = newPrefix + table.getKeyProperty();
    String keySqlScript = table.getKeyColumn() + EQUALS + SqlScriptUtils.safeParam(newKeyProperty);
    var sqlWhere =
        SqlScriptUtils.convertIf(
                fistAnd ? " AND " + keySqlScript : keySqlScript,
                String.format("%s != null", newKeyProperty),
                false)
            + NEWLINE
            + filedSqlScript;

    return sqlWhere + getSpellWhere(info, newPrefix, info.getProperty());
  }

  private String getSpellWhere(TableFieldInfo info, final String prefix, final String property) {
    final String newPrefix = prefix == null ? EMPTY : prefix;
    var newEl = newPrefix + info.getEl();
    // 默认:  AND column=#{prefix + el}
    String sqlScript =
        " AND ( "
            + String.format(info.getCondition(), info.getColumn(), newEl)
            + " OR "
            + String.format(
                " pinyin_code LIKE CONCAT('%%',UPPER(#{%s}),'%%') OR wubi_code LIKE"
                    + " CONCAT('%%',UPPER(#{%s}),'%%')",
                newEl, newEl)
            + " )";
    FieldStrategy whereStrategy = info.getWhereStrategy();
    if (whereStrategy == FieldStrategy.NEVER) {
      return null;
    }
    if (info.isPrimitive()
        || whereStrategy == FieldStrategy.IGNORED
        || whereStrategy == FieldStrategy.ALWAYS) {
      return sqlScript;
    }
    // 查询的时候只判非空
    if (whereStrategy == FieldStrategy.NOT_EMPTY && info.isCharSequence()) {
      return SqlScriptUtils.convertIf(
          sqlScript,
          String.format(
              "%s != null and %s != ''",
              convertIfProperty(prefix, property), convertIfProperty(prefix, property)),
          false);
    }
    String s =
        SqlScriptUtils.convertIf(
            sqlScript, String.format(" %s != null", convertIfProperty(prefix, property)), false);
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("sqlScript: {}", s);
    }
    return s;
  }

  /**
   * 获取拼音查询的sql片段
   *
   * @param prefix 前缀
   * @param property 属性
   * @return sql片段
   */
  private String convertIfProperty(String prefix, String property) {
    return StringUtils.isNotBlank(prefix)
        ? prefix.substring(0, prefix.length() - 1) + "['" + property + "']"
        : property;
  }
}
